#! /bin/sh # This is a shell archive. Remove anything before this line, then feed it # into a shell via "sh file" or similar. To overwrite existing files, # type "sh file -c". # The tool that generated this appeared in the comp.sources.unix newsgroup; # send mail to comp-sources-unix@uunet.uu.net if you want that tool. # Contents: README artcancel spamfind # Wrapped by clewis@crl on Sun May 5 01:16:54 1996 PATH=/bin:/usr/bin:/usr/ucb ; export PATH echo If this archive is complete, you will see the following message: echo ' "shar: End of archive."' if test -f 'README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'README'\" else echo shar: Extracting \"'README'\" \(808 characters\) sed "s/^X//" >'README' <<'END_OF_FILE' XThis software is Copyright Chris Lewis (and others), 1995. X XThis software is intended only for the detection and control of Usenet Xabuse as defined by consensus in news.admin.net-abuse.misc. In Xessence, spamming, forged moderation, and for the use of ISPs to Xenforce their codes-of-conduct/terms-of-service. X XWhile there is nothing earthshattering here in terms of technology - it's Xall a matter of public record, I do not wish to aid and abet abusers Xand forgers. I do not want their abuse to be easy. I can't stop them. XBut I'm not going to help them either. END_OF_FILE if test 808 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'artcancel' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'artcancel'\" else echo shar: Extracting \"'artcancel'\" \(5288 characters\) sed "s/^X//" >'artcancel' <<'END_OF_FILE' X#!/usr/local/bin/perl X# X# artcancel - simple program for cancelling spam. Suitable for ISPs and X# other relatively light use. See README for terms of use. X# X# Synopsis: X# artcancel < article X# X# artcancel reads the article, parses the headers, and produces a X# Usenet "standard spam cancel" which it pipes into rnews. X# If "canceltype" is set to a group name, a canned warning is sent X# without cancelling the article. X# X# One convenient way to use it is to list all of the file names in X# a file, say "f", then in Bourne shell (sh) type: X# X# for i in `cat /tmp/f` X# do X# artcancel < $i X# done X# X# Some highlights of the "standard": X# X# - This is suitable for cancelling: X# spam: make sure you understand what the definitions X# are. See news.admin.net-abuse.announce for X# definitions. X# forged-approval in moderated groups. X# ISPs on their own users (eg: policy violations etc.) X# (see news.admin.net-abuse.announce for definition) X# - Message-Id formed by prepending "cancel.", thus X# -> X# (this allows multiple despammers to operate without flooding X# the net) X# - "cyberspam" placed in Path: so that administrators can opt X# out by aliasing it out. Note: if you're the admin of the site X# that generated the original postings, you should probably just X# remove the "cyberspam!" part. X# - From: and Sender: (if present) must be copied absolutely X# verbatim, lest the cancels not work on some INN systems. X# - X-Canceled-By: and Approved: and body point at issuer of cancel X# X# Original apparently from adcomplain, author unknown X# As modified by Mike Moroney to do cancels X# Rewritten for latest standards/heuristics/perl idiom by Chris Lewis X# X X$originator = "clewis@ferret.ocunix.on.ca"; # often news@yoursite.domain X$rnews = "/usr/local/bin/rnews"; # full path to rnews program X X# Type of messages you're cancelling: X# Possible values are: "SPAM", "FM" (forged moderation) and "ISP" for X# ISP/system administrator cancels. X$canceltype = 'SPAM'; X#$canceltype = 'ott.business.ads'; X X# Don't modify anything after here X# Xif ($canceltype eq 'SPAM') { X $path = "cyberspam!not-for-mail"; X $canprefix = 'cancel.'; X $message = "Spam cancelled by $originator"; X} elsif ($canceltype eq 'FM') { X $path = 'not-for-mail'; X $canprefix = 'can.' . rand(32767) . '.'; X $message = "Moderation approval forged. Cancelled by $originator"; X} elsif ($canceltype eq 'ISP') { X $path = 'not-for-mail'; X $canprefix = 'cancel.'; X $message = "Posting in violation of terms-of-service. Cancelled by $originator"; X} elsif ($canceltype =~ /\./) { X $warningtext = "/home/clewis/despam/warnings/ott.business.ads"; X} X X# Crack headers: Xwhile () { X chop; X if (/^$/) { X last; X } elsif (/^from:\s(.*)$/i) { X $from = $1; X } elsif (/^sender:\s(.*)$/i) { X $sender = $1; X } elsif (/^subject: (.*)$/i) { X $subject = $1; X } elsif (/^newsgroups: (.*)$/i) { X $newsgroups = $1; X } elsif (/^control: (.*)$/i) { X $control = $1; X } elsif (/^message-id: (.*)$/i) { X $id = $1; X if ($id =~ /<(.*)>/) { $id = $1; } X } X if ($warningtext) { X push(@headers, $_); X } X} X Xif ($warningtext) { X $fromcan = $from; X $fromcan =~ s/^from:\s*//i; X $fromcan =~ s/\(.*//; X $fromcan =~ s/^.*.*//; X open(OUT, "|mail -s 'Warning regarding posting to $canceltype' $fromcan") || X die "Can't start email: $!"; X open(IN, "<$warningtext") || die "Can't open $warningtext: $!"; X while() { X print OUT $_; X } X print OUT "===============================================\n"; X foreach $line (@headers) { X print OUT "| $line\n"; X } X print OUT "|\n"; X while(<>) { X print OUT "| $_"; X } X close(OUT); X close(IN); X exit(0); X} X Xif ($control =~ /cancel/) { X printf STDERR "Shouldn't try to cancel cancels - often loops\n"; X exit 1; X} X X# Perform some minor heuristics to avoid subsequent problems with, for X# example, *.test reflectors, people who issue "group,,group,,group" etc. X@groups = split(/,+/,$newsgroups); X X@groups = grep(!/test$/, @groups); # avoid *.test reflectors X# Avoiding lazarus doesn't work. X#@groups = grep(!/^alt.religion.scientology$/, @groups); X X$newsgroups = join(',', @groups); X X$newsgroups = 'news.admin.misc' if !$newsgroups; X Xif ($from && $id && $newsgroups) { X open(RNEWS, "|$rnews") || die "can't pipe to rnews"; X X @t = gmtime(time); X @mon = (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec); X X $str = sprintf("%d %s %d %02d:%02d:%02d %s", X $t[3], $mon[$t[4]], $t[5] + 1900, $t[2],$t[1],$t[0], 'GMT'); X X print RNEWS "Path: $path\n"; X print RNEWS "From: $originator\n"; X print RNEWS "Newsgroups: $newsgroups\n"; X print RNEWS "Subject: cmsg cancel <$id>\n"; X print RNEWS "Date: $str\n"; X print RNEWS "Control: cancel <$id>\n"; X print RNEWS "Message-ID: <$canprefix$id>\n"; X $newsender = $from; X $newsender = $sender if $sender; X print RNEWS "Sender: $newsender\n"; X print RNEWS "X-Cancelled-By: $originator\n"; X print RNEWS "Approved: $originator\n"; X print RNEWS "\n"; X print RNEWS "$message\n"; X close(RNEWS); X exit 0; X} else { X print STDERR "No Message-Id/From to cancel\n"; X exit 1; X} END_OF_FILE if test 5288 -ne `wc -c <'artcancel'`; then echo shar: \"'artcancel'\" unpacked with wrong size! fi chmod +x 'artcancel' # end of 'artcancel' fi if test -f 'spamfind' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'spamfind'\" else echo shar: Extracting \"'spamfind'\" \(7271 characters\) sed "s/^X//" >'spamfind' <<'END_OF_FILE' X#!/usr/local/bin/perl -s X X# EMP-flavour spam detector. This is not for the faint-of-heart. X# Original by Jonathan Kamens, heavily modified by Chris Lewis X# This is the version: %I% %E% X X# Please see README for the terms of use. X X# Act as an exploder news feed from the newsfeeds file. For each X# article file name we get, store a timestamp, the author of the X# article, and the file name, in a log file. Once every X# $report_interval minutes, scan the log to check for warnings that X# need to be sent. X# INN Entry sample: DESPAM::Tx,Wn:/usr/local/news/bin/n_despam X X# Determining warnings: once someone exceeds the specified threshold X# of $spam_threshold different newsgroups posted to in $watch_window X# seconds, that person's postings continue to be stored until he drops X# below the threshold, at which point a warning about the person is X# sent to $report_addr via the command $mailprog. X X# Note that because not all state is kept in the log file, it's X# possible for a spam to be missed if the spam watcher is killed in X# between when a person goes above the threshold and when they drop X# below it again. However, I do not consider this a major concern, X# because other people are watching for spam elsewhere. X X$SEC_PER_MIN = 60; X$MIN_PER_HR = 60; X$HR_PER_DAY = 24; X X$watch_window = 2 * $SEC_PER_MIN * $MIN_PER_HR; X$report_interval = $SEC_PER_MIN * 10; X$spam_threshold = 25; X$report_addr = 'news clewis@ferret.ocunix.on.ca'; X$mailprog = "mailx"; X X$logdir = "/tmp/spamfind"; # log directories X$spooldir = "/usr/local/spool/news"; X X#==================================================== X X$logfile = "$logdir/spamfind.log"; X X$despamcode = 0; X Xrequire 'getopts.pl'; X X&Getopts('Xd'); X X#$opt_d = 1; X X#print "debug is $opt_d\n"; X X$spam_threshold = 2 if $opt_d; X Xif ($despamcode) { X require 'despam.pl'; X} else { X eval "sub despam {}"; X eval "sub udp {}"; X} X Xsub add_file { X local($files, $file) = @_; X local(@files); X X @files = split(/$;/, $files); X X # Strip duplicates X foreach (@files) { X return $files if $_ eq $file; X } X X $files .= "$;" if $files; X $files .= "$file"; X X $files; X} X Xsub num_groups { X X local($files) = @_; X local(@files, $file); X local(%ngs); X X @files = split(/$;/, $files); X return(scalar(@files)) if $opt_d; X X for (@files) { X s/[0-9]+$//; X $ngs{$_}++; X } X X scalar(keys %ngs); X} X X%alerted = (); X Xsub checkfile { X local ($file, $subject, $messageid) = ($_[0], 'Already cancelled'); X $file = "/bnr/news/spool/$file" if $file !~ /^\//; X if (open(ART, "<$file")) { X while() { X chop; X if (/^subject: (.*)$/i) { X $subject = $1; X } elsif (/^message-id: (.*)$/i) { X $messageid = $1; X } X last if /^$/; X } X close(ART); X } X return(($subject, $messageid)); X} X Xsub do_check { X local(%files, %ngs, @ngs, %currentlevels); X local($current_time) = time; X local($_); X local($time, $file, $addr); X local($msg, $key, $value); X X open(LOGFILE, "<$logfile") || die "$0: opening $logfile: $!\n"; X open(NEWLOG, ">$logfile.new") || die "$0: opening $logfile.new: $!\n"; X X while () { X chop; X ($time, $file, $addr, $messageid, $sender, $path, $subject) = X split(/$;/, $_); X next if !$file; X next if $seen{$file}; X $seen{$file} = 1; X next if ($current_time - $time > $watch_window); X print NEWLOG "$_\n" || die "$0: writing to $logfile.new: $!\n"; X $alerted{$addr} = &add_file($alerted{$addr}, $file); X $files{$addr} = &add_file($files{$addr}, $file); X $currentlevels{$addr}++; X } X X close(LOGFILE); X close(NEWLOG); X open(LOGFILE, ">$logfile.cur") || die "$0: opening $logfile.cur: $!\n"; X while(($key, $value) = each(%currentlevels)) { X print LOGFILE $value, "\t", $key, "\n"; X } X close(LOGFILE); X X rename("$logfile.new", $logfile) || die "$0: renaming $logfile.new: $!\n"; X X for $addr (keys %alerted) { X $count = &num_groups($alerted{$addr}); X X print "addr: $addr, count: $count\n" if $opt_d; X X if (($count >= $spam_threshold) && X ($opt_d || (&num_groups($files{$addr}) < $spam_threshold))) { X $msg .= "$count\t$addr:\n"; X @spam = split(/$;/, $alerted{$addr}); X foreach $spamfile (@spam) { X ($subject, $messageid) = &checkfile($spamfile); X next if !$subject; X $msg .= "$spamfile\t$messageid\t$subject\n"; X } X &despam(@spam); X delete $alerted{$addr}; X } X } X X if ($msg) { X if ($opt_d) { X open(MAIL, "|cat"); X print MAIL "|$mailprog -s '$0 report' $report_addr\n"; X } else { X open(MAIL, "|$mailprog -s '$0 report' $report_addr"); X } X X open(DESPAMLOG, ">>$logdir/despam.log"); X X print DESPAMLOG $msg; X print MAIL $msg; X X close(DESPAMLOG); X close(MAIL); X } X X for (keys %alerted) { X if (! $files{$_}) { X delete $alerted{$_}; X } X } X} X Xsub process_input { X local($_); X local($start_time) = time; X local($current_time); X local($line); X local($timeout); X X # Wait up to 2 minutes for previously shutting down despammer to X # finish rewriting the log file. If we don't do this, the first X # interval worth of data is lost because it'll get written on X # a file that will be removed. X X $current_time = time; X while(-r "$logfile.new" && (time - $current_time) < 120) { X sleep(5); X } X unlink("$logfile.new"); X X open(LOGFILE, ">>$logfile") || die "$0: opening $lotfile: $!\n"; X X while () { X chop; X s/\s.*$//; X $filename = $_; X X $current_time = time; X X open(ARTICLE, "$spooldir/$filename") || next; X X ($from, $fromline, $messageid, $sender, $senderline, X $subject, $path, $newsgroups, $control, $references, X $spamsoftware) = (); X X # Burst the headers. We don't do header continuation yet. X X while ($line =
) { X last if ($line =~ /^$/); X chop($line); X if ($line =~ /^from:\s*(.*)$/i) { X $fromline = $line; # save whole line intact X $from = $1; X } elsif ($line =~ /^message-id:\s*(.*)$/i) { X $messageid = $1; X } elsif ($line =~ /^subject:\s*(.*)$/i) { X $subject = $1; X } elsif ($line =~ /^references:\s*(.*)$/i) { X $references = $1; X } elsif ($line =~ /^path:\s*(.*)$/i) { X $path = $1; X } elsif ($line =~ /^newsgroups:\s*(.*)$/i) { X $newsgroups = $1; X } elsif ($line =~ /^News-Posting-Software:\s*(.*)\s*$/i) { X $spamsoftware = $1; X } elsif ($line =~ /^sender:\s*(.*)$/i) { X $senderline = $line; # save whole line intact X # Canonicalize Sender: X $sender = $1; X $sender =~ s/^.*<(.*)>.*$/$1/; X $sender =~ s/\s*\(.*\)\s*//; X } elsif ($line =~ /^control:\s*(.*)/i) { X $control = $1; X } X } X X print "$filename: $from\n" if $opt_d; X X next if $control && control !~ /supersedes/; # Do not monitor control messages X # next if $references || $subject =~ /re:/i; # Yeah, I've seen Re: spams X X if ($from) { X print "SAVE: $filename $from\n" if $opt_d; X print LOGFILE join($;, ($current_time, $filename, $from)), "\n" || X die "$0: writing to $logfile: $!\n"; X } else { X print LOGFILE join($;, ($current_time, $filename, 'nofrom')), X "\n" || die "$0: writing to $logfile: $!\n"; X } X X &udp; X X close(ARTICLE); X if ($current_time - $start_time > $report_interval) { X $timeout++; X last; X } X } X X close(LOGFILE); X return $timeout; X} X Xwhile (1) { X $timeout = &process_input; X &do_check; X exit if (! $timeout); X} END_OF_FILE if test 7271 -ne `wc -c <'spamfind'`; then echo shar: \"'spamfind'\" unpacked with wrong size! fi chmod +x 'spamfind' # end of 'spamfind' fi echo shar: End of archive. exit 0 -- Chris Lewis: _Una confibula non sat est_